import logging

from django.db.models import Q
from drf_yasg import openapi
from drf_yasg.utils import swagger_auto_schema
from rest_framework import viewsets, status
from rest_framework.decorators import action
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response

from BYD_ERP.utils.pagination import GlobalPagination
from erp_system.models import MenuModel, PermissionsModel
from BYD_ERP.apps.erp_system.serializer.menu_serializer import MenuSerializer


# 得到日志输出对象logger
logger = logging.getLogger('erp')

"""
    功能菜单的模型类

    1.新增
    2.查询某个功能菜单
    3.查询所有功能菜单
    4.查询某个父菜单下的所有子菜单
    5.查询所有的顶级菜单列表
    6.删除某一个功能菜单
    7.批量删除多个功能菜单， 传参：ids=[1,2,5]
    8.修改功能菜单
"""


class MenuView(viewsets.ModelViewSet):
    """
        create:
        新增菜单：

        参数：为menu对象，其中delete_flag,create_time,update_time不用传参，return：添加之后的对象

        retrieve:
        查询单个功能菜单：

        list:
        查询所有的菜单：

        如果参数中有pid，则查询某个父菜单下的所有子菜单列表，pid=0表示查询顶级菜单列表

        update:
        修改菜单：

        partial_update：
        局部修改菜单，修改任意指定的属性

        destroy:
        删除指定ID的菜单：

        get_menu_by_permission：
        当前登录用户，根据权限查询那些拥有get权限的功能菜单列表（返回的是树形菜单）

        功能菜单列表信息，status：200（成功），return：功能菜单信息列表

    """

    # permission_classes = [IsAuthenticated]
    queryset = MenuModel.objects.filter(delete_flag=0).all()
    serializer_class = MenuSerializer
    pagination_class = GlobalPagination

    def get_queryset(self):
        logger.info(f'当前登录的用户是：{self.request.user.username}')
        """
            两种情况：
            1.查询所有菜单列表：不需要传任何参数；
            2.查询某个父菜单下的所有子菜单：需要传递一个参数，名字叫pid
                pid==0的时候，查询到的是顶级菜单。
        """
        query_param = self.request.query_params.get('pid', None)
        if query_param:
            pid = int(query_param)
            if pid == 0:  # 查询所有顶级菜单列表
                return MenuModel.objects.filter(parent__isnull=True, delete_flag=0).all()
            else:  # 查询某个父菜单下的所有子菜单
                return MenuModel.objects.filter(parent__id=pid, delete_flag=0).all()
        else:
            return MenuModel.objects.filter(delete_flag=0).all()

    def destroy(self, request, *args, **kwargs):
        # 将删除标记修改为1： delete_flag=1.
        menu = self.get_object()
        menu.delete_flag = '1'
        menu.save()
        # 可能该菜单下还有许多子菜单，这些子菜单同样需要修改
        MenuModel.objects.filter(parent__id=menu.id).update(delete_flag=1)
        return Response(status=status.HTTP_204_NO_CONTENT)

    del_ids = openapi.Schema(type=openapi.TYPE_OBJECT, required=['ids'], properties={
        'ids': openapi.Schema(type=openapi.TYPE_ARRAY, items=openapi.Schema(type=openapi.TYPE_INTEGER),
                              description="选择那些需要删除的ID（主键）列表")
    })

    # action装饰器可以接收两个参数：
    # methods：声明改action对应的请求方式，列表传递
    # detail：声明改action的路径是否与单一资源对应，及是否是xxx/<pk>/action方法名/
    #     True：表示路径格式是xxx/<pk>/action方法名/
    #     False：表示路径格式是xxx/action方法名/
    @swagger_auto_schema(method='delete', request_body=del_ids, operation_description="批量删除")
    @action(methods=['delete'], detail=False)
    def multiple_delete(self, request, *args, **kwargs):
        delete_ids = request.data.get('ids')
        if not delete_ids:
            return Response(data={'detail': '参数错误，ids为必传参数'}, status=status.HTTP_400_BAD_REQUEST)
        elif not isinstance(delete_ids, list):
            return Response(data={'detail': 'ids格式错误，必须是list'}, status=status.HTTP_400_BAD_REQUEST)
        # 先删除传递过来的功能菜单
        MenuModel.objects.filter(id__in=delete_ids).update(delete_flag=1)
        # 再删除对应的所有的子菜单
        for mult_id in delete_ids:
            MenuModel.objects.filter(parent__id=mult_id).update(delete_flag=1)
        return Response(status=status.HTTP_204_NO_CONTENT)
        pass

    @action(methods=['get'], detail=False)
    def get_menu_by_permission(self, request, *args, **kwargs):
        # 当前登录用户，根据权限查询那些拥有get权限的功能菜单列表

        # 第一步：查询当前登录用户，拥有的权限id列表
        # request.user就可以获得当前登录的用户
        # values_list返回的是列表，values返回的是字典（以字段名作为键）
        permission_ids = request.user.roles.values_list('permissions', flat=True).distinct()

        # 第二步：查询拥有get权限的功能菜单的id列表
        # 两个filter是一个并且的关系
        # 注意，特殊情况，父菜单没有任何method，父菜单的权限也应该要出现
        # | 是“或者”的意思
        menu_ids = PermissionsModel.objects.filter(id__in=permission_ids)\
                                           .filter(Q(method='GET') | Q(is_menu=True)).values_list('menu', flat=True)
        # 第三步：根据id查询菜单列表，列表里面的元素是对象
        # delete_flag=0表示没删
        menu_list = MenuModel.objects.filter(id__in=menu_ids, delete_flag=0).all()
        # 用序列化把对象变成字典
        # 因为是多条数据，所以要加上many=True
        serializer = MenuSerializer(instance=menu_list, many=True)

        # 要转换成树形结构
        tree_dict = {}    # 一个字典代表一个父菜单
        tree_data = []
        for item in serializer.data:
            tree_dict[item['id']] = item
        for i in tree_dict:
            if tree_dict[i]['parent']:
                pid = tree_dict[i]['parent']
                parent = tree_dict[pid]
                parent.setdefault('children', []).appenf(tree_dict[i])
            else:    # 这里是说没有父菜单
                tree_data.append(tree_dict[i])

        return Response(tree_data)

